﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System.Xml;
using System.IO;
using DocumentFormat.OpenXml;

namespace UpdateCustomXmlNamespace
{
    class Program
    {
        static int Main(string[] args)
        {
            if (args.Length < 3)
            {
                Console.WriteLine("Usage: UpdateCustomXmlNamespace document currentNs newNs");
                Console.WriteLine(" eg: UpdateCustomXmlNamespace OrderTemplate.docx http://www.infosupport.nl/xmlquery urn:fleXdoc");
                return 1;
            }
            string currentNs = args[1];
            string newNs = args[2];

            int replaceCount = 0;
            OpenSettings openSettings = new OpenSettings();
            openSettings.MarkupCompatibilityProcessSettings = new MarkupCompatibilityProcessSettings(
                MarkupCompatibilityProcessMode.ProcessAllParts,
                FileFormatVersions.Office2007); // 2010 not yet supported
            openSettings.MaxCharactersInPart = 0;
            WordprocessingDocument doc = WordprocessingDocument.Open(args[0], true, openSettings);

            Console.WriteLine("Processing headers... ");
            foreach (HeaderPart header in doc.MainDocumentPart.GetPartsOfType<HeaderPart>())
            {
                replaceCount += UpdatePart(header, currentNs, newNs);
            }
            Console.WriteLine("Processing footers... ");
            foreach (FooterPart footer in doc.MainDocumentPart.GetPartsOfType<FooterPart>())
            {
                replaceCount += UpdatePart(footer, currentNs, newNs);
            }

            Console.WriteLine("Processing main document part... ");
            replaceCount += UpdatePart(doc.MainDocumentPart, currentNs, newNs);

            if (replaceCount > 0)
            {
                Console.WriteLine("Registering the new namespace...");
                XmlDocument docSettings = new XmlDocument();
                docSettings.Load(doc.MainDocumentPart.DocumentSettingsPart.GetStream());
                XmlNode schemaNode = docSettings.CreateNode(XmlNodeType.Element, "attachedSchema", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
                XmlAttribute schemaValueAttr = (XmlAttribute)docSettings.CreateNode(XmlNodeType.Attribute, "val", "http://schemas.openxmlformats.org/wordprocessingml/2006/main");
                schemaValueAttr.Value = newNs;
                schemaNode.Attributes.Append(schemaValueAttr);
                docSettings.DocumentElement.AppendChild(schemaNode);
                MemoryStream settingsStream = new MemoryStream();
                docSettings.Save(settingsStream);
                docSettings = null;
                settingsStream.Seek(0, SeekOrigin.Begin);
                doc.MainDocumentPart.DocumentSettingsPart.FeedData(settingsStream);
                doc.MainDocumentPart.DocumentSettingsPart.RootElement.Reload();
            }

            doc.Close();

            Console.WriteLine("Finished. {0} CustomXml-element(s) have been updated.", replaceCount);

            return 0;
        }


        private static int UpdatePart(OpenXmlPart part, string currentNs, string newNs)
        {
            int replaceCount = 0;

            IEnumerable<CustomXmlElement> elements =
                from e
                in part.RootElement.Descendants<CustomXmlElement>()
                where e.Uri == currentNs
                select e;

            foreach (CustomXmlElement element in elements)
            {
                element.Uri = newNs;
                replaceCount++;
            }

            part.RootElement.Save();

            return replaceCount;
        }
    }
}
